﻿using System;
using System.Collections.Generic;
using System.Data.SqlClient;
using System.Linq;
using AutoMapper;
using CollegeSim.Domain.DTO;
using CollegeSim.Domain.Interfaces.Repositories;

namespace CollegeSim.Persistence.Repositories
{
    // Author: Ryan Redburn
    // Project: CollegeSim
    // Date: 2/5/2014

    #region [ Updates ]

    // Author: Ryan Redburn
    // Date: 2/22/2014
    // Revision: Introduced exception handling for invalid method arguments

    // Author: Ryan Redburn
    // Date: 2/26/2014
    // Revision: Added exception handling for unique constraints

    // Author: Ryan Redburn
    // Date: 5/6/2014
    // Revisions: Changed repository to reflect version 2.0 changes. No major revisions.

    // Author: Ryan Redburn
    // Date: 5/20/2014
    // Revisions: Added exception handling for database connection problems and additional commenting.

    #endregion

    /// <summary>
    /// Repository used to interact with instructor objects. Provides CRUD and search functionality,
    /// </summary>
    public class InstructorRepository : IInstructorRepository
    {
        #region [ Methods ]

        /// <summary>
        /// Returns all Instructors.
        /// </summary>
        /// <returns>IEnumerable of InstructorDto.</returns>
        public IEnumerable<InstructorDTO> FindAll()
        {
            IEnumerable<InstructorDTO> instructors = null;

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Map retrieved dbset to targeted return type
                    instructors = Mapper.Map<IEnumerable<InstructorDTO>>(context.Instructors);
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }

            return instructors;
        }

        /// <summary>
        /// Returns all Instructors with the given name.
        /// </summary>
        /// <param name="firstName">Instructor first name.</param>
        /// <param name="lastName">Instructor last name.</param>
        /// <returns>IEnumerable of InstructorDto.</returns>
        public IEnumerable<InstructorDTO> FindByName(string firstName, string lastName)
        {
            // Confirm valid name values
            if (!String.IsNullOrWhiteSpace(firstName) && !String.IsNullOrWhiteSpace(lastName))
            {
                IEnumerable<InstructorDTO> instructors = null;

                // Open entity connection
                using (var context = new CollegeSimEntities())
                {
                    try
                    {
                        // Map retrieved dbset to targeted return type
                        instructors = Mapper.Map<IEnumerable<InstructorDTO>>(context.Instructors
                            .Where(i => i.Person.FirstName == firstName && i.Person.LastName == lastName));
                    }
                    catch (AutoMapperMappingException ex)
                    {
                        // Check for database connection exception
                        if (ex.InnerException.InnerException is SqlException)
                        {
                            throw new InvalidOperationException(
                                "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                        }
                    }
                }

                return instructors;
            }

            // Throw exception for invalid firstName value
            if (String.IsNullOrWhiteSpace(firstName))
            {
                throw new ArgumentNullException("firstName",
                    "firstName does not accept a null or empty string as an argument.");
            }

            // Throw exception for invalid lastName value
            if (String.IsNullOrWhiteSpace(lastName))
            {
                throw new ArgumentNullException("lastName",
                    "lastName does not accept a null or empty string as an argument.");
            }

            return null;
        }

        /// <summary>
        /// Returns an instructor with the given ID. Returns a null InstructorDTO if no instructor with the given ID is found.
        /// </summary>
        /// <param name="instructorID">Instructor integer id.</param>
        /// <returns>InstructorDto.</returns>
        public InstructorDTO FindByID(int instructorID)
        {
            // Throw exception on invalid instructorID value
            if (instructorID < 0)
                throw new ArgumentOutOfRangeException("instructorID",
                    "instructorID cannot be less than zero.");

            InstructorDTO instructor = null;

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Map retrieved dbset to targeted return type
                    instructor = Mapper.Map<InstructorDTO>(context.Instructors
                        .SingleOrDefault(i => i.InstructorID == instructorID));
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }

            return instructor;
        }

        /// <summary>
        /// Saves a new Instructor.
        /// </summary>
        /// <param name="newInstructor">New Instructor to save.</param>
        public void Insert(InstructorDTO newInstructor)
        {
            // Throw exception for null dto
            if (newInstructor == null)
                throw new ArgumentNullException("newInstructor",
                    "newInstructor does not accept a null dto as an argument.");

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Add new instructor and commit changes
                    context.Instructors.Add(Mapper.Map<Instructor>(newInstructor));
                    context.SaveChanges();
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }
        }

        /// <summary>
        /// Updates an existing Instructor.
        /// </summary>
        /// <param name="instructorUpdate">Instructor to update.</param>
        public void Update(InstructorDTO instructorUpdate)
        {
            // Throw exception for null dto
            if (instructorUpdate == null)
                throw new ArgumentNullException("instructorUpdate",
                    "instructorUpdate does not accept a null dto as an argument.");

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Confirm instructor does exist
                    var instructor =
                        context.Instructors.SingleOrDefault(i => i.InstructorID == instructorUpdate.InstructorID);

                    // Throws an exception if no matching entry is found
                    if (instructor == null)
                        throw new InvalidOperationException("No entry matching the given instructor was found.");

                    // Update instructor and commit changes
                    instructor.Person.FirstName = instructorUpdate.FirstName;
                    instructor.Person.LastName = instructorUpdate.LastName;
                    instructor.Person.PhoneNumber = instructorUpdate.PhoneNumber;
                    instructor.Person.Email = instructorUpdate.Email;
                    instructor.OfficeNumber = instructorUpdate.OfficeNumber;

                    context.SaveChanges();
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }
        }

        /// <summary>
        /// Deletes an existing Instructor.
        /// </summary>
        /// <param name="instructorDel">Instructor to delete.</param>
        public void Delete(InstructorDTO instructorDel)
        {
            // Throw exception for null dto
            if (instructorDel == null)
                throw new ArgumentNullException("instructorDel",
                    "instructorDel does not accept a null dto as an argument.");

            // Open entity connection
            using (var context = new CollegeSimEntities())
            {
                try
                {
                    // Confirm instructor does exist
                    var instructor = context.Instructors.SingleOrDefault(i => i.InstructorID == instructorDel.InstructorID);

                    // Throws an exception if no matching entry is found
                    if (instructor == null)
                        throw new InvalidOperationException("No entry matching the given instructor was found.");

                    // Delete instructor and commit changes
                    context.Instructors.Remove(instructor);
                    context.SaveChanges();
                }
                catch (AutoMapperMappingException ex)
                {
                    // Check for database connection exception
                    if (ex.InnerException.InnerException is SqlException)
                    {
                        throw new InvalidOperationException(
                            "A database connection issue has occurred. Check your network connection and try again, or contact the application developer.");
                    }
                }
            }
        }

        #endregion
    }
}
